package com.mrx.commons.aop.service;

import com.mrx.commons.annotations.AfterCommit;
import com.mrx.commons.trace.TraceUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.util.*;
import java.util.concurrent.ExecutorService;

/**
 * {@link AfterCommit 注解实现类}
 *
 * @author Mr.X
 * @since 2023-12-27 13:38
 **/
@Aspect
public class AfterCommitAop {

    private static final Logger logger = LoggerFactory.getLogger(AfterCommitAop.class);

    @Pointcut("@annotation(com.mrx.commons.annotations.AfterCommit)")
    public void afterCommit() {
    }

    @Around("afterCommit()")
    public Object around(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        if (!signature.getReturnType().equals(void.class)) {
            logger.warn("被 AfterCommit 注解标识的方法必须是 void 方法, 否则其返回值将会被丢弃!");
        }
        logger.debug("afterCommit aop: {}", signature.getMethod().getName());
        TransactionSynchronizationService.INSTANCE.submit(wrap(joinPoint));
        return null;
    }

    private Runnable wrap(ProceedingJoinPoint joinPoint) {
        return () -> {
            try {
                joinPoint.proceed();
            } catch (Throwable e) {
                logger.error("方法执行失败: ", e);
            }
        };
    }

    private static class TransactionSynchronizationService implements TransactionSynchronization {

        private static final ThreadLocal<List<Runnable>> tasks = ThreadLocal.withInitial(ArrayList::new);

        private static final ExecutorService pool = TraceUtils.newFixedTracerPool(2, "TransactionSync");

        private static final TransactionSynchronizationService INSTANCE = new TransactionSynchronizationService();

        public void submit(Runnable runnable) {
            if (TransactionSynchronizationManager.isSynchronizationActive()) {
                logger.info("事务处于激活中, 启用 AfterCommit");
                List<Runnable> task = tasks.get();
                if (task == null) {
                    task = new ArrayList<>();
                }
                task.add(runnable);
                TransactionSynchronizationManager.registerSynchronization(this);
            } else {
                logger.info("事务暂未激活, 直接执行方法");
                invokeAll(Collections.singletonList(runnable));
            }
        }

        @Override
        public void afterCommit() {
            logger.debug("After commit: {}", TransactionSynchronizationManager.getCurrentTransactionName());
            Optional.ofNullable(tasks.get()).ifPresent(this::invokeAll);
        }

        @Override
        public void afterCompletion(int status) {
            logger.debug("afterCompletion: {}", status);
        }

        private void invokeAll(Collection<Runnable> tasks) {
            for (Runnable task : tasks) {
                pool.execute(task);
            }
        }

    }

}
